Skip to content

Conversation

TheBlueMatt
Copy link
Collaborator

When paying a BOLT 12 invoice an amount greater than any one of
multiple blinded paths, we need to track how much is in-flight
across the paths when retrying. Here we do so, tracking blinded
path usage in a new field in InFlightHtlcs.

Fixes #2737

In the next commit we'll track blinded path in-flights in
`InFlightHtlcs` as well as unblinded hops, so here we convert
`InFlightHtlcs` to a named-field struct so that we can add more
fields to it with less confusion.
Its not clear why this needs to be serialized at all (it should
generally always be generated live when needed), but its
serialization isn't forward-compatible, so really needs to be
dropped so that we can add additional field(s) to `InFlightHtlcs`.
@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Sep 15, 2025

I've assigned @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

When paying a BOLT 12 invoice an amount greater than any one of
multiple blinded paths, we need to track how much is in-flight
across the paths when retrying. Here we do so, tracking blinded
path usage in a new field in `InFlightHtlcs`.

Fixes lightningdevkit#2737
Copy link

codecov bot commented Sep 15, 2025

Codecov Report

❌ Patch coverage is 96.18321% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.36%. Comparing base (5ae19b4) to head (4cbd556).

Files with missing lines Patch % Lines
lightning/src/routing/router.rs 97.56% 2 Missing and 1 partial ⚠️
lightning/src/routing/scoring.rs 75.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4072      +/-   ##
==========================================
+ Coverage   88.33%   88.36%   +0.02%     
==========================================
  Files         177      177              
  Lines      131896   132011     +115     
  Branches   131896   132011     +115     
==========================================
+ Hits       116512   116645     +133     
+ Misses      12728    12704      -24     
- Partials     2656     2662       +6     
Flag Coverage Δ
fuzzing 21.60% <0.00%> (-0.02%) ⬇️
tests 88.19% <96.18%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 2nd Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 3rd Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 4th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 5th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 6th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 7th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 8th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 9th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Contributor

@tankyleo tankyleo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just two questions for my own understanding thank you

Comment on lines +1688 to +1691
if usage.amount_msat > hint.payinfo.htlc_maximum_msat {
return u64::max_value();
} else if total_inflight_amount_msat > hint.payinfo.htlc_maximum_msat {
return score_params.considered_impossible_penalty_msat;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In both cases, total amount flowing over the channel exceeds our current estimate of the channel's available liquidity, so why return u64::max_value() vs score_params.considered_impossible_penalty_msat ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is consistent with what we do elsewhere - we only use u64::MAX when the amount for this HTLC is too large (which shouldn't happen, the router should never try to do that) whereas we use considered_impossible_penalty_msat if we have existing HTLCs that might saturate a channel. considered_impossible_penalty_msat is high enough that we should always prefer any other possible route, but allows us to still try to send if we don't have any other option. In the case of in-flight HTLCs pushing us over a limit, we might as well try cause those other attempts may fail before the new HTLC gets there.

Of course for blinded paths it maybe shouldn't matter anyway - the blinded path candidates should be within the context of a single payment (because we should have a unique blinded path for each payment) so if we have no option but to over-saturate a blinded path probably we're screwed. Its still worth trying, imo, because the recipient may have given us a blinded path with a too-low htlc_maximum_msat (ie cause they rounded-down for privacy) or could have re-used a blinded path across payments.

Comment on lines +392 to +394
if tail.hops.len() > 1 {
// Single-hop blinded paths aren't really "blinded" paths, as they terminate at the
// introduction point. In that case, we don't need to track anything.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm ok I would have thought a single-hop blinded path is [intro] -> [destination]; need to read up further.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, its confusing...

@ldk-reviews-bot
Copy link

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@ldk-reviews-bot
Copy link

✅ Added second reviewer: @valentinewallace

let mut cumulative_msat = 0;
if let Some(tail) = &path.blinded_tail {
cumulative_msat += tail.final_value_msat;
if tail.hops.len() > 1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this method should take into account trampoline hops, might be easy to do here

if tail.hops.len() > 1 {
// Single-hop blinded paths aren't really "blinded" paths, as they terminate at the
// introduction point. In that case, we don't need to track anything.
let last_hop = path.hops.last().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last unblinded hop could be the last trampoline hop instead, IIUC

inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::HintMaxHTLC { amount_msat: 500 },
};
inflight_scorer.channel_penalty_msat(&candidate, empty_usage, &());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this value be checked?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

make InFlightHtlc take into account blinded data
4 participants